import { ConfigPreview, PuckPreview } from "@/docs/components/Preview";
import { Puck } from "@/puck";

# Component Configuration

Puck's core behaviour is configured via the [Config](/docs/api-reference/configuration/config). This describes:

- which components are available to Puck
- how to render each component
- which fields to show when the user selects a component
- additional information, like [category grouping](categories)

The [Config](/docs/api-reference/configuration/config) is provided via the `config` prop to the main Puck components:

- [`<Puck>`](/docs/api-reference/components/puck) reads the Config and renders an editor UI. The user interacts with the editor to produce a [data payload](/docs/api-reference/data-model/data).
- [`<Render>`](/docs/api-reference/components/render) takes a [data payload](/docs/api-reference/data-model/data) and renders it according to the provided Config.

## The `render` function

Components can be defined via the `components` object in [Config](/docs/api-reference/configuration/config). Every definition must provide a [`render` function](/docs/api-reference/configuration/component-config#renderprops):

```tsx showLineNumbers copy {4-6}
const config = {
  components: {
    HeadingBlock: {
      render: () => {
        return <h1>Hello, world</h1>;
      },
    },
  },
};
```

This tells Puck that **HeadingBlock** is a valid component, and describes how to render it.

When the user drags the component onto the preview and hits **Publish** in the editor UI via the `<Puck>` component, this Config will produce a [data payload](/docs/api-reference/data-model/data) like this:

```json copy
{
  "content": [
    {
      "type": "HeadingBlock",
      "props": {
        "id": "HeadingBlock-1234"
      }
    }
  ],
  "root": {}
}
```

The data payload and Config together tell `<Render>` how to render the page. It can also be provided to `<Puck>` as an [initial `data` payload](/docs/api-reference/components/puck#data).

<PuckPreview
  label="Try interacting with the heading"
  config={{
    components: {
      HeadingBlock: {
        render: () => {
          return <span>Hello, world</span>;
        },
      },
    },
  }}
  data={{
    content: [{ type: "HeadingBlock", props: { id: "HeadingBlock-1" } }],
    root: { props: {} },
  }}
>
  <Puck.Preview />
</PuckPreview>

### TypeScript

If you're using TypeScript, we recommend strictly typing your config:

```tsx copy {1,3-5} /Components/2
import type { Config } from "@measured/puck";

type Components = {
  HeadingBlock: {};
};

const config: Config<Components> = {
  components: {
    HeadingBlock: {
      render: () => {
        return <h1>Hello, world</h1>;
      },
    },
  },
};
```

## Adding fields

[Fields](/docs/api-reference/fields) allow users to provide input to components. The value of each field is passed in as a prop to the `render` function.

You can define a field via the [`fields` parameter](/docs/api-reference/configuration/component-config#fields):

```tsx showLineNumbers copy {5-7} /title/2,3
const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      render: ({ title }) => {
        return <h1>{title}</h1>;
      },
    },
  },
};
```

This will render a [Text field](/docs/api-reference/fields/text) when the user selects an instance of the **HeadingBlock** component in the editor UI.

<ConfigPreview
  label='Text field example'
  componentConfig={{
    fields: {
      title: {
        type: "text",
      },
    },
    render: ({ title }) => {
      return <span>{title}</span>;
    },

}}
/>

When the user modifies the input, the editor will produce a data payload like this:

```json copy {7}
{
  "content": [
    {
      "type": "HeadingBlock",
      "props": {
        "id": "HeadingBlock-1234",
        "title": "Hello, world"
      }
    }
  ],
  "root": {}
}
```

### TypeScript

It's best to define the props for the component if using TypeScript. This enables strict type checking for your fields.

```tsx copy {5}
import type { Config } from "@measured/puck";

type Components = {
  HeadingBlock: {
    title: string;
  };
};

const config: Config<Components> = {
  // ...
};
```

## Setting default props

Default props allow you to set an initial value for a prop when a new component is added.

Provide an object to the [`defaultProps`](/docs/api-reference/configuration/component-config#fields) parameter to configure this:

```tsx showLineNumbers copy {9-11}
const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      defaultProps: {
        title: "Hello, world",
      },
      render: ({ title }) => {
        return <h1>{title}</h1>;
      },
    },
  },
};
```

Unlike [default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters), `defaultProps` are stored in the data payload and will populate the Puck fields.

<ConfigPreview
  label="Text field example"
  componentConfig={{
    fields: {
      title: {
        type: "text",
      },
    },
    defaultProps: {
      title: "Hello, world",
    },
    render: ({ title }) => {
      return <span>{title}</span>;
    },
  }}
/>
